﻿/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Framework.Console
{

	public sealed class Console
	{
		private VariableGroupCollection variableGroups = new VariableGroupCollection();
		/// <summary>
		/// Gets the collection of <see cref="VariableGroup"/>s.
		/// </summary>
		public VariableGroupCollection VariableGroups { get { return variableGroups; } }

		private bool registered = false;

		/// <summary>
		/// Registers a variable in a category.
		/// </summary>
		/// <param name="variable">The variable to register.</param>
		/// <param name="groupName">The group to register it in.</param>
		public void RegisterVariable( Variable variable, string groupName )
		{
			if( registered )
				throw new InvalidOperationException();

			if( variable == null )
				throw new ArgumentNullException();

			if( groupName == null )
				throw new ArgumentNullException();
			if( groupName == string.Empty )
				throw new ArgumentException();

			VariableGroup group;

			group = variableGroups[groupName];
			if( group != null )
			{
				var existingVar = group[variable.Name];
				
				if( existingVar == variable )
					return;

				if( existingVar != null )
					throw new InvalidOperationException( "The group already contains a variable with the same name." );
			}

			if( group == null )
				group = variableGroups.FindOrCreateGroup( groupName );

			group.AddVariable( variable );
		}

		public void FinishRegistration()
		{
			foreach( var group in variableGroups )
			{
				foreach( var var in group )
				{
					var.RaiseChanged();
				}
			}

			registered = true;
		}

	}

	public sealed class VariableGroup : ReadOnlyCollection<Variable>
	{
		internal VariableGroup( string name )
			: base( new List<Variable>() )
		{
			this.name = name;
		}

		private string name;
		public string Name { get { return name; } }

		internal void AddVariable( Variable var )
		{
			int idx = Items.BinarySearch( v => string.Compare( v.Name, var.Name ) );

			if( idx >= 0 )
				throw new InvalidOperationException( "Can't add a variable with the same name as another." );

			Items.Insert( ~idx, var );			
		}

		/// <summary>
		/// Finds a <see cref="Variable"/> by name.
		/// </summary>
		/// <param name="name">
		/// The name of the <see cref="Variable"/> to find.
		/// </param>
		/// <returns>
		/// The <see cref="Variable"/> with that name, or
		/// <c>null</c> if it does not exist in this collection.
		/// </returns>
		public Variable this[string name]
		{
			get
			{
				int idx = Items.BinarySearch( v => string.Compare( v.Name, name ) );
				return idx >= 0 ? Items[idx] : null;
			}
		}
	}

	public sealed class VariableGroupCollection : ReadOnlyCollection<VariableGroup>
	{
		internal VariableGroupCollection()
			: base( new List<VariableGroup>() )
		{
		}

		internal VariableGroup FindOrCreateGroup( string name )
		{
			int idx = Items.BinarySearch( g => string.Compare( g.Name, name ) );

			if( idx < 0 )
			{
				idx = ~idx;
				Items.Insert( idx, new VariableGroup( name ) );
			}

			return Items[idx];
		}

		/// <summary>
		/// Finds a <see cref="VariableGroup"/> by name.
		/// </summary>
		/// <param name="name">
		/// The name of the <see cref="VariableGroup"/> to find.
		/// </param>
		/// <returns>
		/// The <see cref="VariableGroup"/> with that name,
		/// or <c>null</c> if it does not exist in this collection.
		/// </returns>
		public VariableGroup this[string name]
		{
			get
			{
				int idx = Items.BinarySearch( g => string.Compare( g.Name, name ) );
				return idx >= 0 ? Items[idx] : null;
			}
		}
	}

}